/*
 * Copyright (C) 2010 Imavis Srl
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.imavis.video.parse.mpeg4.impl;

import com.imavis.video.parse.impl.BitBuffer;
import java.nio.ByteBuffer;
import static com.imavis.video.parse.mpeg4.impl.Mpeg4Utils.*;

/**
 *
 * @author Aldo Brucale
 */
public final class VideoObjectLayer {
    private boolean lowDelay;
    private int timeIncrementResolution;
    private int timeIncrement;
    private int timeIncrementBits;
    private Shape shape;
    private int width;
    private int height;

    private VideoObjectLayer() {
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public Shape getShape() {
        return shape;
    }

    public int getTimeIncrement() {
        return timeIncrement;
    }

    public int getTimeIncrementBits() {
        return timeIncrementBits;
    }

    public int getTimeIncrementResolution() {
        return timeIncrementResolution;
    }

    /**
     * @return true if has b-frames
     */
    public boolean isLowDelay() {
        return lowDelay;
    }

    public void setTimeIncrementResolution(int timeIncrementResolution) {
        if (timeIncrementResolution == 0) {
            throw new IllegalArgumentException(
                    "vop_time_increment_resolution is zero");
        }
        this.timeIncrementResolution = timeIncrementResolution;
        this.timeIncrementBits =
                32 - Integer.numberOfLeadingZeros(timeIncrementResolution - 1);
    }

    public static VideoObjectLayer parse(final ByteBuffer bb) {
        final BitBuffer b = new BitBuffer(bb);

        final VideoObjectLayer ret = new VideoObjectLayer();

        final boolean randomAccess = b.getBit(); // random access
        // random access
        final int voType = b.getBits(8);
        final int verId;
        if (b.getBit()) { // is object layer identifier
            verId = b.getBits(4);
            final int priority = b.getBits(3); // video priority
        } else {
            verId = 1;
        }

        final int aspectRatioInfo = b.getBits(4);
        if (aspectRatioInfo == 0xF) {
            final int par_width = b.getBits(8);
            final int par_height = b.getBits(8);
        }

        if (b.getBit()) { // vol_control_parameters
            final int chromaFormat = b.getBits(2);  // chromaFormat
            ret.lowDelay = b.getBit();    // low delay
            if (b.getBit()) {   // vbv_parameters
                b.getBits(15);  //  first_half_bit_rate
                checkMarkerBit(b);
                b.getBits(15);  //  latter_half_bit_rate
                checkMarkerBit(b);
                b.getBits(15);  //  first_half_vbv_buffer_size
                checkMarkerBit(b);
                b.getBits(15);  //  latter_half_vbv_buffer_size
                checkMarkerBit(b);
                b.getBits(15);  //  first_half_vbv_occupancy
                checkMarkerBit(b);
                b.getBits(15);  //  latter_half_vbv_occupancy
                checkMarkerBit(b);
            }
        }

        ret.shape = Shape.values()[b.getBits(2)]; // video_object_layer_shape
        if (ret.getShape() == Shape.GREY && verId != 0x01) {
            b.getBits(4); // video_object_layer_shape_extension
        }

        checkMarkerBit(b);
        ret.setTimeIncrementResolution(b.getBits(16));  // vop_time_increment_resolution

        checkMarkerBit(b);
        if (b.getBit()) { // fixed_vop_rate
            // fixed_vop_time_increment
            ret.timeIncrement = b.getBits(ret.getTimeIncrementBits());
        } else {
            ret.timeIncrement = 1;
        }

        if (ret.getShape() != Shape.BIN_ONLY) {
            if (ret.getShape() == Shape.RECT) {
                checkMarkerBit(b);
                ret.width = b.getBits(13);
                checkMarkerBit(b);
                ret.height = b.getBits(13);
                checkMarkerBit(b);
            }
//            final boolean interlaced = b.getBit();
//            final boolean obmcDisable = b.getBit();
//
//            final int spriteEnable = (verId == 1) ? b.getBits(1) : b.getBits(2);
//            if (spriteEnable != 0) {
//                throw new IllegalArgumentException("Sprites not supported");
//            }
//
//            if ((verId != 1) && (ret.shape != Shape.RECT)) {
//                final boolean sadctDisable = b.getBit();
//            }
//
//            if (b.getBit()) { // not_8_bit
//                final int quantPrecision = b.getBits(4);
//                final int bitsPerPixel = b.getBits(4);
//            }
        }

        return ret;
    }
}
